from sumolib import net;
from pyproj import Proj;
from xml.dom import minidom
from xml.dom.minidom import Document
import string;
import subprocess;
import math;
import simplejson as json

class SimConfig:

	def __init__(self, formData):


		self._dataProcessor = DataProcessor(formData);
		self._dataProcessor.DownloadMap();
		self._NetFile = self._dataProcessor.ConvertMap();
		self._dataProcessor.downloadGoogleSteps();
		self._Network = net.readNet(self._NetFile);
		self._flowFactory = FlowFactory(self._Network);
				#Remove this
		pointFactory = PointFactory(self._Network.getProjParameter(), self._Network.getNetOffset()); 
		
		#edgesId = self._flowFactory.makeFlows('pointFirstHop',self._dataProcessor.getSimInfo(), self._dataProcessor.getGoogleSteps());
		edgesId = self._flowFactory.makeFlows('pointToEdge',self._dataProcessor.getSimInfo(), self._dataProcessor.getGoogleSteps());
		
		#Type options are 'pointFirstHop' and 'pointToEdge'
		print "Found Edge ID = "+ str(edgesId);
		print "";
		
		
		for edgeId in edgesId:
			EdgeSource = self._Network.getEdge(edgeId).getFrom().getCoord();
			EdgeDest = self._Network.getEdge(edgeId).getTo().getCoord();
			EdgeS = pointFactory.newPoint('sumo',EdgeSource);
			EdgeD = pointFactory.newPoint('sumo',EdgeDest);
			print "Edge Source = "+str(EdgeS.getOpen());
			print "Edge Destination = "+ str(EdgeD.getOpen());
			print "";
		"""
		"""
		
class GoogleSteps:
	
	def __init__ (self, jsonFile):
		self._steps = jsonFile["routes"][0]["legs"][0]["steps"];
		self._start = [];
		self._end = [];
		for i in range(len(self._steps)):
			self._start.append(self._steps[i]["start_location"]);
			self._end.append(self._steps[i]["end_location"]);	
		#print self._start[0];
	def	getSrc(self): 
		return self._start[0];
	
	def getFirstHop(self):
		return self._end[0];
		
class EdgeComputer(object):
	
	def __init__(self,networkNodes, networkEdges):
		
		self._networkNodes = list(networkNodes);
		self._networkEdges = list(networkEdges);
		self._sourceEdges=[];
		self._destEdges=[];

		
	def getFlows(self):
		return self._sourceEdges[0];
		
	#Computes sd.dp
	
	def dot(self,s,d,p):
		sd=[];dp=[];
		sd.append(d[0]-s[0]);
		sd.append(d[1]-s[1]);
		dp.append(d[0]-p[0]);
		dp.append(p[1]-d[1]);
		dot = sd[0]*dp[0] + sd[1]*dp[1];
		return dot;
	
	#Distance between two points a and b
	def dist(self,a,b):
		dist = math.sqrt( (a[0] - b[0])**2 + (a[1] - b[1])**2 );
		return dist;
		
	def sortCoordSource(self,node):
		coords = node.getCoord();
		netX = coords[0];
		netY = coords[1];
		sourcePoint = self._currentReference.getSumo();
		currentDist = math.sqrt(((netX-sourcePoint[0])**2)+((netY-sourcePoint[1])**2));
		node.dist = currentDist;
		return currentDist;	

	def sortCoordFirst(self,node):
		coords = node.getCoord();
		netX = coords[0];
		netY = coords[1];
		firstHop = self._flowCfgs[self._currentFlowCfg].getFirstHop().getSumo();
		currentDist = math.sqrt(((netX-firstHop[0])**2)+((netY-firstHop[1])**2));
		node.dist = currentDist;
		return currentDist;
	
	def sortEq(self,Eq) :
		a=Eq['a'];
		b=Eq['b'];
		c=Eq['c'];
		sourcePoint = self._flowCfgs[self._currentFlowCfg].getSource().getSumo();
		dist = math.fabs( a*sourcePoint[0] + b*sourcePoint[0] + c)/math.sqrt(a**2 + b**2);
		Eq['dist'] = dist;
		return dist;
	
	def getIds(self,edgeList):
		edgeIds =[];
		for edge in edgeList:
			id = edge.getId().split('#')[0];
			edgeIds.append(id);
		return edgeIds;
	
	def sortByDist(self,dict):
		source = dict['source'];
		destination = dict['dest'];
		point = self._currentReference.getSumo();
		SDP = self.dot(source,destination,point);
		DSP = self.dot(destination,source,point);
		if (SDP < 0):
			return self.dist(destination,point);
		if (DSP < 0):
			return self.dist(source,point);
		else:
			a = source[1]-destination[1];
			b= source[0] - destination[0];
			c= source[0]*destination[1]-source[0]*destination[1];
			dist = math.fabs( a*source[0] + b*source[0] + c)/math.sqrt(a**2 + b**2);
			return dist;

	def getNClosestNodes(self, sourcePoint,N):
		self._currentReference = sourcePoint;
		NodesBySource = list(self._networkNodes);
		NodesBySource.sort(key=self.sortCoordSource);
		NodesBySource = NodesBySource[0:N];
		return NodesBySource;
	
	def projectPointToEdge(self,sourcePoint):	# Tested and working

		SNodes = self.getNClosestNodes(sourcePoint,2);
		Spoints = [];
		candidateEdges =[];
		edgesByDistance =[];
		for i in range(len(SNodes)):
			Spoints.append(SNodes[i].getCoord());
			candidateEdges += SNodes[i].getIncoming() + SNodes[i].getOutgoing();
		
		for edge in candidateEdges:
			source = edge.getFrom().getCoord();
			destination = edge.getTo().getCoord();
			edgeId = edge.getId();
			temp = {
			'source' : source,
			'dest' : destination,
			'id' : edgeId
			};
			edgesByDistance.append(dict(temp));
		self._currentReference = sourcePoint
		edgesByDistance.sort(key=self.sortByDist);
		return edgesByDistance[0]['id'];
	
	def pointFirstHopToEdge(self, source, firstHop):
		print source.getOpen();
		print firstHop.getOpen();
		SNodes = self.getNClosestNodes(source,2);
		FNodes = self.getNClosestNodes(firstHop,2);
		SNodeEdgesList = [];
		FNodeEdgesList = [];
		
		print "";print "";
		print "[][][][][][][][][][][][][][][]";
		print "Source Node Coordinates : "
		for SNode in SNodes:
			print "<sourceNode>";
			print SNode.getCoord();
			print "</sourceNode>";
			NodeEdges = SNode.getOutgoing();
			edgeIds = self.getIds(SNode.getOutgoing());
			obj = {
			'edges':NodeEdges,
			'edgeIds': edgeIds
			};
			print "Node : "
			for edge in obj['edges']:
				print edge.getFrom().getCoord();
				print edge.getTo().getCoord();
			print "";
			SNodeEdgesList.append(obj);
		print "[][][][][][][][][][][][][][][]";
		print "[][][][][][][][][][][][][][][]";
		print "First Hop Node Coordinates : ";
		for FNode in FNodes:
			print "<firstHopNode>";
			print FNode.getCoord();
			print "</firstHopNode>";
			NodeEdges = FNode.getIncoming();
			edgeIds = self.getIds(FNode.getOutgoing());
			obj = {
			'edges':NodeEdges,
			'edgeIds': edgeIds
			};
			print "Node : "
			for edge in obj['edges']:
				print edge.getFrom().getCoord();
				print edge.getTo().getCoord();
			print "";
			FNodeEdgesList.append(obj);
		print "[][][][][][][][][][][][][][][]";print "";print "";
		
		for i in range(len(SNodeEdgesList)):
			print "IN pointFirstHopToEdge : ";
			for j in range(len(FNodeEdgesList)):
				for edgeObj in FNodeEdgesList[j]['edges']:
					print "Edge source: "+ str(edgeObj.getFrom().getCoord());
					print "EdgeDestination: "+ str(edgeObj.getTo().getCoord());
				Common = list(set(SNodeEdgesList[i]['edgeIds']) & set(FNodeEdgesList[j]['edgeIds']));
				if (len(Common)==1):
					print "**************************************";
					print "We have found our edge";
					print Common;
					print "**************************************";
					return Common;
		
			
	def computeEdge(self,sourcePoint,firstHop):
		empX = sourcePoint[0];
		empY = sourcePoint[1];
		firstHopX = firstHop[0];
		firstHopY = firstHop[1];
		print 'flowSource = ';
		print sourcePoint;
		print "flowFirstHop = ";
		print firstHop;
		NodesBySource= list(self._networkNodes);
		NodesByFirst = list(NodesBySource); #Construct a new list don't do shallow copying
		NodesBySource.sort(key=self.sortCoordSource);
		NodesByFirst.sort(key=self.sortCoordFirst);
		SNodes = NodesBySource[0:3];						#Candidate Source Nodes
		FNodes = NodesByFirst[0:3];							#Candidate First Hop Nodes
		candidateNodeIds = [];
		Edges = [];
		# Get All edges connected to nearby nodes
		for node in SNodes:
			#print sumoToOpen(node.getCoord());
			candidateNodeIds.append(float(node.getId()));
			incEdges = node.getIncoming();
			outEdges = node.getOutgoing();
			Edges += incEdges + outEdges;		
			Edges += list(set(Edges)); # Remove duplicates if present
	
		for FNode in FNodes:
			FEdges = self.getIds(FNode.getIncoming());
			#print "{0},{1}".format(sumoToOpen(FNode.getCoord())['lat'],sumoToOpen(FNode.getCoord())['lon']);
			#print FEdges;
			for SNode in SNodes:
				#print "Source : ";
				#print "{0},{1}".format(sumoToOpen(SNode.getCoord())['lat'],sumoToOpen(SNode.getCoord())['lon']);
				SEdges = self.getIds(SNode.getOutgoing());
				#print SEdges;
				Common = list(set(FEdges) & set(SEdges));
				if (len(Common) == 1):
					print "{0},{1}".format(sumoToOpen(FNode.getCoord())['lat'],sumoToOpen(FNode.getCoord())['lon']);
					print "{0},{1}".format(sumoToOpen(SNode.getCoord())['lat'],sumoToOpen(SNode.getCoord())['lon']);
					return Common[0];

	#If edge isn't found based on previous method resort to this one.	
		
		candidateEdges = [];
		for edge in Edges:
			if ((float(edge.getTo().getId()) in candidateNodeIds) and (float(edge.getFrom().getId()) in candidateNodeIds) ):
				candidateEdges.append(edge);
			
		
		candidateEdges = list(set(candidateEdges));
		#print candidateEdges;
		
		
		candidateEdgeEqs = [];	
		for edge in candidateEdges:
			fromX = float(edge.getFrom().getCoord()[0]);
			fromY=float(edge.getFrom().getCoord()[1]);
			toX= float(edge.getTo().getCoord()[0]);
			toY = float(edge.getTo().getCoord()[1]);
			Eq = {
			'a': fromY-toY,
			'b': toX - fromX,
			'c': fromX*toY-toX*fromY,
			'id': edge.getId(),
			'from': edge.getFrom(),
			'to': edge.getTo()
			}
			candidateEdgeEqs.append(Eq);	
		candidateEdgeEqs.sort(key=self.sortEq);
		TheChosenEdge = candidateEdgeEqs[0]['id'];
		return TheChosenEdge;
	
	def computeDestEdge():
		a=1;
	
	def verifyFlow():
		a=1;


class PointFactory(object):
	def __init__(self, projParam, netOffset):
		self._projParam = projParam
		self._netOffset = netOffset;
	def newPoint(self, type, coord=None):
		return Point(self._projParam, self._netOffset, type, coord);
		
class Point(object):
	
	def __init__(self,projParam, netOffset,type,coord = None):
		self._projParam = ''.join(projParam);
		self._netOffset = float(netOffset[0]),float(netOffset[1]);	#Construct new copy of list
		self._x=0;
		self._y=0;
		self._lon=0;
		self._lat=0;
		if (coord != None):
			if (type == 'sumo'):
				self._x = float(coord[0]);
				self._y = float(coord[1]);
				self.setOpen(coord);
			if (type == 'open'):
				self._lat = float(coord[0]);
				self._lon = float(coord[1]);
				self.setSumo(coord);
	
	def setSumo(self,coord):	
		xOffset = self._netOffset[0];
		yOffset = self._netOffset[1];
		p = Proj(str(self._projParam));
		x,y = p(self._lon,self._lat);
		x = x + xOffset;
		y = y + yOffset;
		self._x = x;
		self._y = y;

	
	def setOpen(self, coord):
		xOffset = self._netOffset[0];
		yOffset = self._netOffset[1];
		p = Proj(str(self._projParam));
		x1 = self._x - xOffset;
		y1 = self._y - yOffset;
		self._lon,self._lat = p(x1,y1,inverse=True);
		
	def getOpen(self):
		coord = self._lat,self._lon;
		return coord;
		
	def getSumo(self):
		coord = self._x,self._y;
		return coord;



class FlowFactory(object):	#Keep track of created flows
	
	def __init__(self,Network):
		projParam = Network.getProjParameter()
		netOffset = Network.getNetOffset();
		self._pointFactory = PointFactory(projParam,netOffset) ;
		self._edgeComputer = EdgeComputer(Network.getNodes(), Network.getEdges());
		
		self._currentFlowCfgs=[];
		self._currentFlows=[];
		self._flowId=0;
		self._defaultStart = 0;
		self._defaultEnd = 1000;
	
	def makeFlows(self,type,simInfo, googleStepList):
		edges=[];
		
		for i in range(len(simInfo)):
			print "Iteration : ";
			fHop = simInfo[i]['firstHop'];
			startTime = simInfo[i]['startTime'];
			endTime = simInfo[i]['endTime'];
			rate = simInfo[i]['rate'];
			sLat = googleStepList[i].getSrc()['lat'];
			sLon = googleStepList[i].getSrc()['lng'];
			sCoor = sLat,sLon;
			fLat = googleStepList[i].getFirstHop()['lat'];
			fLon = googleStepList[i].getFirstHop()['lng'];
			fCoor = fLat,fLon;
			source = self._pointFactory.newPoint('open',sCoor);
			firstHopIdiot = self._pointFactory.newPoint('open',fHop);
			firstHopGoogle = self._pointFactory.newPoint('open',fCoor);
			if (type=='pointToEdge'):
				ClosestEdge = self._edgeComputer.projectPointToEdge(source);
			elif (type=='pointFirstHop'):
				ClosestEdge = self._edgeComputer.pointFirstHopToEdge(source,firstHopIdiot);
			elif (type=='pointFirstHopGoogle'):
				ClosestEdge = self._edgeComputer.pointFirstHopToEdge(source,firstHopGoogle);
			edges.append(ClosestEdge);
			print "*******************************";print "";
		return edges;
		
	def newFlowCfg(self, source, firstHop, lastHop, destination, rate, startTime=None, endTime=None):
		src= self._pointFactory.newPoint('open',source);
		dest= self._pointFactory.newPoint('open',destination);
		fHop = self._pointFactory.newPoint('open',firstHop);
		lHop = self._pointFactory.newPoint('open',lastHop);
		if (startTime == None):
			if (endTime == None):
				flow = FlowCfg(src, fHop, lHop, dest, rate,self._defaultStart, self._defaultEnd);
				self._currentFlowCfgs.append(flow);
				return flow;
			else :
				flow = FlowCfg(src, fHop, lHop,dest, rate,self._defaultStart,endTime);
				self._currentFlowCfgs.append(flow);
				return flow;
		
		if (endTime == None):
			if (startTime != None):
				flow = FlowCfg(src, fHop, lHop,dest, rate,startTime, self.defaultEnd);
				self._currentFlowCfgs.append(flow);
				return flow;
		
		
		flow = FlowCfg(src, fHop, lHop,dest,rate,startTime, endTime);
		self._currentFlowCfgs.append(flow);
		return flow;
	
	def newFlow(self,srcEdge, destEdge, rate, startTime=None, endTime=None):
		if (startTime == None):
			if (endTime == None):
				flow = Flow(self._flowId,srcEdge,destEdge,rate, self._defaultStart, self._defaultEnd);
				self._flowId = self._flowId +1;
				self._currentFlows.append(flow);
				return flow;
			else:
				flow = Flow(self._flowId,srcEdge,destEdge,rate, self._defaultStart,endTime);
				self._flowId = self._flowId +1;
				self._currentFlows.append(flow);
				return flow;
				
		if (endTime == None):
			if (startTime != None):
				flow = Flow(self._flowId,srcEdge,destEdge,rate, startTime,self._defaultEnd);
				self._flowId = self._flowId +1;
				self._currentFlows.append(flow);
				return flow;
				
		flow = Flow(self._flowId,srcEdge,destEdge,rate, startTime ,endTime);
		self._flowId = self._flowId +1;
		self._currentFlows.append(flow);
		return flow;
		
	def getFlowCfgs(self):
		return self._currentFlowCfgs;

class Flow(object):

	def __init__(self,id, srcEdge,destEdge,rate,start,end):
		self._id = id;
		self._sourceEdge  = srcEdge;
		self._destEdge = destEdge;
		self._rate = rate;
		self._startTime = start;
		self._endTime = end;

	def getStartTime():
		return self._startTime;
		
	def getEndTime():
		return self._endTime;
		
	def getId():
		return self._id;
	
	def getSourceEdge():
		return self._sourceEdge;
	
	def getDestEdge():
		return self._destEdge;
	
	def getRate():	
		return self._rate;


class FlowCfg(object):

	def __init__(self,src, fHop, lHop,dest,rate, start,end): 
		self._source = src;
		self._fHop = fHop;
		self._lHop = lHop;
		self._destination = dest;
		self._rate = rate;
		self._startTime = start;
		self._endTime = end;
		
	def getSource(self):
		return self._source;
	
	def getFirstHop(self):
		return self._lHop;
	
	def getLastHop(self):
		return self._lHop;
	
	def getDest(self):
		return self._destination;
	
	def getRate(self):
		return self._rate;
		
	def getStartTime(self):
		return self._startTime;
		
	def getEndTime(self):
		return self._endTime;

class DataProcessor(object):
	
	def __init__(self, formData):

		self._googleStepsList =[];
		self._UserId = formData.get('UserId')[0];
		self._flowSourcePoints = self.readPoints(formData.get('flowSources')[0]);
		self._flowFirstHopPoints = self.readPoints(formData.get('flowFirstHops')[0]);
		self._flowDestinationPoints = self.readPoints(formData.get('flowDestinations')[0]);
		self._flowRates = self.readValues('float',formData.get('flowRates')[0]);
		self._startTime = self.readValues('float',formData.get('startTime')[0]);
		self._endTime = self.readValues('float',formData.get('endTime')[0]);		
		self._osmExportLink,self._osmMapLink = self.writeLinks(formData.get('top')[0],formData.get('right')[0],formData.get('bottom')[0],formData.get('left')[0]);
		self._SimulationFolder = "MapInterface/Simulation_"+self._UserId;
		
	
	def getGoogleSteps(self):
		return self._googleStepsList;
		
	def getSimInfo(self):
		simInfo = []; 		# List of dictionaries that contain startTime, endTime and rate
		for i in range(len(self._flowRates)):
			dict = {
				'firstHop': self._flowFirstHopPoints[i],
				'rate': self._flowRates[i],
				'startTime': self._startTime[i],
				'endTime': self._endTime[i]
			};
			simInfo.append(dict);
		return simInfo;	
			
		
		
	def downloadGoogleSteps(self):
		linkBase = "http://maps.googleapis.com/maps/api/directions/json?";
		sources = self._flowSourcePoints;
		dests = self._flowDestinationPoints;
		for i in range(len(sources)):
			sourceCoords = "("+str(float(sources[i][0]))+","+str(float(sources[i][1]))+")";
			destCoords = "("+ str(float(dests[i][0]))+","+str(float(dests[i][1]))+")";
			link = linkBase +"origin="+sourceCoords+"&destination="+destCoords+"&sensor=false";
			downloadGoogleStepsCmd = "wget \""+ link + "\" -O "+self._SimulationFolder+"/tempJsonStep"+str(len(self._googleStepsList))+".json";
			subprocess.call(downloadGoogleStepsCmd, shell = True);
			jsonFile = json.loads(open(self._SimulationFolder+"/tempJsonStep"+str(len(self._googleStepsList))+".json",'r').read());
			self._googleStepsList.append(GoogleSteps(jsonFile));
	
	def writeLinks(self,top,right,bottom,left):
		osmBoundaries = left+","+bottom+","+right+","+top;
		osmExportLink = "http://api.openstreetmap.org/api/0.6/map?bbox="+osmBoundaries;
		osmMapLink = "http://www.openstreetmap.org/?box=yes&maxlat="+top+"&maxlon="+right+"&minlat="+bottom+"&minlon="+left;
		return osmExportLink,osmMapLink;
		
	def readPoints(self,data):
		flowSources = string.replace(data,'(','');
		flowSources = string.replace(flowSources,')','');
		flowSourcePoints = flowSources.split('s')
		flowSourcePointsArray = [];
		for p in flowSourcePoints:
			pointFormat = p.split(',')
			flowSourcePointsArray.append(pointFormat);
		return flowSourcePointsArray;
		
	def readValues(self,outputType,data):
		vals = data.split('s');
		outputVals = [];
		if (outputType == 'float'):
			for a in vals:
				outputVals.append(float(a)); 
			return outputVals;
		return vals;

	def makeFlowCfgs(self,flowCfgFactory):
		for i in range(len(self._flowSourcePoints)):
			src= self._flowSourcePoints[i];
			dest= self._flowDestinationPoints[i];
			rate= self._flowRates[i];
			flowCfgFactory.newFlowCfg(src, fhop, lhop, dest, rate);

	
	def DownloadMap(self):
		makeFolderCmd = "mkdir "+self._SimulationFolder;
		subprocess.call(makeFolderCmd, shell = True);
		downloadMapCmd = "wget "+self._osmExportLink+ " -O "+ self._SimulationFolder + "/map.osm";
		subprocess.call(downloadMapCmd, shell = True);
	
	def ConvertMap(self):
		makeFolderCmd = "mkdir "+self._SimulationFolder;
		subprocess.call(makeFolderCmd, shell = True);
		convertMapCmd = "/opt/local/bin/netconvert --osm-files "+self._SimulationFolder+"/map.osm --proj.utm -o "+self._SimulationFolder+"/netMap.net.xml";
		if (subprocess.call(convertMapCmd, shell = True) == 0):
			print "Map Converted successfully";
			return self._SimulationFolder+"/netMap.net.xml";
		
		return None;

class XMLWriter:
	
	def __init__(self):
		self._flowFile="";
		
	def writeFlowDef(self, fileName, flowList):
		self._flowFile = fileName;
		doc = Document();
		rootTag = doc.createElement('flowdefs');
		doc.appendChild(rootTag);
		intervalTag= doc.createElement('interval');
	
		for i in range(len(flowList)): 
			intervalTag.setAttribute('begin',flowList[i].getStartTime());
			intervalTag.setAttribute('end',flowList[i].getEndTime());
			flowTag = doc.createElement('flow');
			flowTag.setAttribute('id',flowList[i].getId());
			flowTag.setAttribute('from',flowList[i].getSourceEdge());
			flowTag.setAttribute('to',flowList[i].getDestEdge());
			flowTag.setAttribute('number',(float(flowList[i].getEndTime())-float(flowList[i].getStartTime()))/float(flowList[i].getRate()));
			intervalTag.appendChild(flowTag);
		
		f= open(fileName,'w');
		f.write(doc.toxml());
		
		return filename;
	
	
		
		
xOffset = -728301.16
yOffset = -3750839.32
projParam = "+proj=utm +zone=36 +ellps=WGS84 +datum=WGS84 +units=m +no_defs";
netOffset = xOffset,yOffset;
pointFactory = PointFactory(projParam,netOffset);
coord = 33.899884,35.47468;
myPoint = pointFactory.newPoint('open',coord);
#print myPoint.getSumo();
#print myPoint.getOpen();


